Introduction + Methods

This is the 3rd time I’m trying to estimate the needed sample size for our study. At this point, you should (hopefully) know the drill…
This time we will try to estimate our needed N with another simulation approach using deviance to a “true score” which we will get with sampling a lot.

We use this sample sequence:

sample_sequence
##  [1]   20   30   40   50   60   70   80   90  100  150  200  250  500  750 1000
## [16] 1250 1500 1750 2000 2250 2500 2750 3000 5000

And for each of these sample sizes, we simulate 400 times (while higher numbers would lead to a more robust/ reliable finding, I still need to make sure that it can still be computable)…

Here are our assumed population distribution, which we will sample from again:

normal <- round(rnorm(n_population, mean = 9,sd = 1.2))
normal <- pmin(pmax(normal, min_likert), max_likert)

strong_pol <- round(rbeta(n_population, shape1 = .1, shape2 = .1)*10) +1
strong_pol <- pmin(pmax(strong_pol, min_likert), max_likert)

rare <- c(round(rnorm(n_population *(1- prop_minority), mean = 2,sd = 1.3)), 
           round(rnorm(n_population *prop_minority, mean = 11,sd = .5)))
rare <- pmin(pmax(rare, min_likert), max_likert)

normal_2 <- round(rnorm(n_population, mean = 8,sd = 1))
normal_2 <- pmin(pmax(normal_2, min_likert), max_likert)


Pop_df <- data.frame(`Normal Distribution`= normal,
                     `Normal Distribution 2` = normal_2,
                     `Rare Polarization` = rare,
                     `Strong Polarization` = strong_pol)

names(Pop_df) <- c("None", "None 2", "Rare", "Strong Pol.")



Pop_df_plot <- Pop_df %>% pivot_longer(everything(), values_to = "Rating", names_to = "Distr") %>% 
   mutate(Distr = factor(Distr, levels = c("None", "None 2","Rare", "Strong Pol.")))

Pop_df_plot %>% ggplot(aes(Rating, fill = Distr))+
   geom_bar(width= .75)+
   facet_grid(rows = vars(Distr))+
   scale_x_continuous(n.breaks = 11, expand = c(0,0))+
   theme_minimal()+
   theme(strip.text = element_blank(),
         axis.text.y = element_blank(),
         legend.position = "none",
         plot.margin = margin(1, 2, 0, 0),
         axis.text.x = element_text(size = 12))+
   geom_text(aes(x=6,y = 3500, label= Distr, color = Distr), size = 6.8)+
   scale_fill_manual(values = colors)+
   scale_color_manual(values = colors)+
   ylab("Count")

# max_cols <- max(sampled)
# 
# # Register parallel backend with the desired number of cores
# num_cores <- detectCores()-1
# 
# cl <- makeCluster(num_cores)
# registerDoParallel(cl)
# 
# #rotate Pop_df, so our function works (each row needs to be a different distribution, instead of each col)
# rot_Pop_df <- as.data.frame(t(Pop_df))
# 
# risk_dist_tr <- data.frame(risk_distribution = 1:4,
#            risk_dist_transl = rownames(rot_Pop_df))
# 
# # Define function to process each combination of risk_distribution, samplesize and replications per setting
# sample_and_replicate_for_all_risks <- function(i) {
#   sampled_matrix_list <- list()
# 
#   for (j in 1:nrow(rot_Pop_df)) {
#     mat <- replicate(replications_per_setting,
#                      sample(1:n_population, size = sampled[i, 1], replace = TRUE)) #create matrix of our samples with replacing, times n - replications
# 
#     sampled_table <- rot_Pop_df[j, mat] #using the matrix, collect the values from our risk distribution matrix (as a vector though...)
#     sampled_matrix <- as.data.frame(matrix(sampled_table,
#                                            nrow = replications_per_setting,
#                                            ncol = sampled[i, 1],
#                                            byrow = TRUE,
#                                            dimnames = NULL)) #create df out of these vectors instead of flat vectors
# 
# 
#     if (ncol(sampled_matrix) < max_cols) {
#       padding_matrix <- matrix(NA,
#                                nrow = nrow(sampled_matrix),
#                                ncol = max_cols - ncol(sampled_matrix))
#       sampled_matrix <- cbind(sampled_matrix, padding_matrix)
#     } #if matrix is not wide enough for our end result matrix, padd it with NA columns, so binding rows is doable (needs same amount of ncols)
# 
#     sampled_matrix <- cbind(matrix(sampled[i,1], nrow = replications_per_setting),
#                             matrix(j, nrow = replications_per_setting),
#                             sampled_matrix) #bind columns with additional information such as sample size and which risk_distribution was sampled
# 
#     colnames(sampled_matrix) <- c("sample_size", "risk_distribution", paste0("rating_", 1:max_cols)) #rewrite colnames so it is identical to the bigh matrix
#     sampled_matrix_list[[j]] <- sampled_matrix #store in list
#   }
#   return(do.call(rbind, sampled_matrix_list)) #after all risk distributions are sampled from, bind them all and return the output
# 
# }
# 
# # Perform parallel processing using foreach, iterating through the different samplesizes
# result <- foreach(i = 1:nrow(sampled), .combine = rbind) %dopar% {
#   sample_and_replicate_for_all_risks(i)
# }
# 
# # Stop the parallel backend
# stopCluster(cl)
# 
# result <- mutate_all(result, as.numeric)
# 
# 
# vis_miss(result[,seq(from =3, to = ncol(result), length.out =20)], show_perc_col = F)
#taken from my previous work

# calc_bimodality_coefficient <- function(vec){
#    skew <- skew(vec, na.rm = TRUE, type = 3)
#    kurtosis <- kurtosi(vec, na.rm = TRUE, type = 3)
#    n <- sum(!is.na(vec))
#    return((skew^2+1) / (kurtosis + ((3*((n-1))^2)/((n-2)*(n-3))) ))
# }
# 
# # Created this one "from scratch", as we work with a smaller scale of 11 instead of 101, the calculation of polarization does not take too long anymore
# 
# calc_polarization <- function(vec){
#    vec2 <- as.vector(vec)
#    freq_vec <- agrmt::collapse(vec2, pos = c(1:11))
#    return(agrmt::polarization(freq_vec))
# }
# 
# # this code is commented, as the computation takes too long, and I hate waiting while kniting. I have saved a sepparate rds file, which will be read in instead. Readers who want to uncomment section, select the lines to uncomment and press Ctrl + Shift + C (on Windows/Linux) or Cmd + Shift + C (on macOS)
# #
# # #apply the functions to our result matrix, therefore calculate for each drawn sample the polarization metrics
# BC_result <- apply(result[,-c(1:2)], 1, calc_bimodality_coefficient)
# sum(is.na(BC_result))
# 
# 
# # Register parallel backend with the desired number of cores
# num_cores <- detectCores()-1
# 
# cl <- makeCluster(num_cores)
# registerDoParallel(cl)
# 
# # Perform parallel processing using foreach, iterating through the different drawn samples
# polarization_result <- foreach(i = 1:nrow(result), .combine = rbind) %dopar% {
#   calc_polarization(result[i, -c(1:2)])
# }
# sum(is.na(polarization_result))
# 
# # Stop the parallel backend
# stopCluster(cl)
# 
# 
# combined_result_measures <- cbind(polarization_result,
#                                   BC_result,
#                                   result[,1:2]
#                                   )
# sum(is.na(combined_result_measures))
# 
# 
# combined_result_measures <- combined_result_measures %>%
#    left_join(risk_dist_tr, by = "risk_distribution") %>%
#    mutate(fsample_size = factor(sample_size, ordered = T))
# 
# saveRDS(combined_result_measures, "saved_RDS\\combined_result_measures_power_analysis_deviance.rds")

combined_result_measures <- readRDS("saved_RDS\\combined_result_measures_power_analysis_deviance.rds")



vis_miss(combined_result_measures, sort_miss = F)

Results

In the following density plots, we see the distribution of each calculated polarization measure (from the book “Triggerpunkte” from Mau et al.), which ranges from 0 (not polarized, people are in agreement) to 1 (highest polarization). We can see that the more we sample, the more the results converge to a “true” score for said assumed population distribution.

combined_result_measures %>% 
   ggplot(aes(polarization_result))+
   geom_density(aes(fill = fsample_size), alpha = .4)+
   facet_grid(rows =vars(risk_dist_transl))

And for the bimodality Coefficient as well:

combined_result_measures %>% 
   ggplot(aes(BC_result))+
   geom_density(aes(fill = fsample_size), alpha = .4)+
   facet_grid(rows =vars(risk_dist_transl))

In the following, we will calculate the mode of each density plot (that is, where the polarization measures converges or has a high probability that it ends up on X, if we were to sample it fewer times). We can then plot these findings, and should look at the point where the modes converges towards the “true score” (in our case, the sample size of 5000).

combined_result_measures <- combined_result_measures %>% 
   mutate(grouped_distXsample = paste(risk_dist_transl, fsample_size, sep = "_"))

splitted <- split(combined_result_measures$polarization_result, f = combined_result_measures$grouped_distXsample)
splitted2 <- split(combined_result_measures$BC_result, f = combined_result_measures$grouped_distXsample)

mode_dens_res <- data.frame()

for (i in 1:length(splitted)) {

   dens_res <- density(splitted[[i]])
   dens_res2 <- density(splitted2[[i]])
   
   dens_df <- data.frame(group = names(splitted)[i],
                         mode_dens_pol = dens_res$x[which.max(dens_res$y)],
                         mode_dens_BC = dens_res2$x[which.max(dens_res2$y)])
   
   mode_dens_res <- rbind(mode_dens_res, dens_df)
   
}

cleaned_mode_dens_res <- mode_dens_res %>% 
   separate(group, sep = "_", into = c("Distribution", "fsample_size")) %>% 
   mutate(Sample_Size = as.integer(fsample_size)) %>% 
   pivot_longer(cols = starts_with("mode_dens"),
                names_prefix = "mode_dens_",
                names_to = "measure",
                values_to = "value")
   

cleaned_mode_dens_res %>% 
   ggplot(aes(Sample_Size, value, group = Distribution, col = Distribution))+
   geom_line(size= 1.1)+
   facet_grid(rows = vars(measure))+
   scale_x_continuous(breaks = sample_sequence)+
   theme_minimal()+
   scale_color_manual(values = colors)

Probability of Sample being in True Score Interval

While the mode could a be good indicator, one might also argue that it is more relevant to have a good chance of capturing the true polarization value. That is, the probability that the measure is only off by only .1 (meaning it is always within a interval of + 0.05 and - 0.05) from the true score. So let’s calculate the % where the 400 simulations per sample size was within this interval. One could then interpret the results as: the probability of coming to the same polarization value (with an interval of .1) compared to if we had sampled 5’000 people.

Interval of .1

# calculate the "true score" value of said polarization measure for each population distribution

true_score <- combined_result_measures %>% 
   filter(sample_size == 5000) %>% 
   group_by(risk_dist_transl) %>% 
   summarize(mean_true_score_BC = mean(BC_result),
             mean_true_score_pol = mean(polarization_result))




combined_result_measures_within_interval <- combined_result_measures %>% 
   left_join(true_score, by = c("risk_dist_transl")) %>% 
   mutate(within_BC = if_else(BC_result >= mean_true_score_BC- lower_and_upper_interval_limit &
                               BC_result <= mean_true_score_BC + lower_and_upper_interval_limit, 1, 0),
          within_pol = if_else(polarization_result >= mean_true_score_pol- lower_and_upper_interval_limit &
                               polarization_result <= mean_true_score_pol + lower_and_upper_interval_limit, 1, 0))

summarized_within_interval <-
   combined_result_measures_within_interval %>%
   group_by(risk_dist_transl, sample_size) %>%
   summarize(count_within_BC = sum(within_BC),
      count_within_pol = sum(within_pol),
      perc_within_BC = sum(within_BC) / replications_per_setting * 100,
      perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
   pivot_longer(cols = starts_with("perc"),
      names_to = "measure",
      values_to = "percentage",
      names_prefix = "perc_within_")

summarized_within_interval %>% ggplot(aes(factor(sample_size), percentage, col = risk_dist_transl, group = risk_dist_transl))+
   geom_point()+
   geom_line()+
   facet_wrap(~measure)+
   theme_minimal()+
   scale_y_continuous(breaks = seq(0, 100, by = 10))+
   labs(y = "percentage of simulations within 0.05 interval of true score",
        x = "sample size",
        title = "Interval of +- 0.05")+
   scale_color_manual(values = colors)+
   theme(legend.position = "none")

Interval of .05

As the interval of .05 (which results of a coverage of .1) was “arbitrarily” taken, let’s use the same procedure for an interval of .025 (or a span of only 0.05). In this case, one would expect that we need a bigger sample size, as the margin for “error” is smaller where we would be confident enough that we have captured the true score.

combined_result_measures_within_interval <- combined_result_measures %>% 
   left_join(true_score, by = c("risk_dist_transl")) %>% 
   mutate(within_BC = if_else(BC_result >= mean_true_score_BC- lower_and_upper_interval_limit2 &
                               BC_result <= mean_true_score_BC + lower_and_upper_interval_limit2, 1, 0),
          within_pol = if_else(polarization_result >= mean_true_score_pol- lower_and_upper_interval_limit2 &
                               polarization_result <= mean_true_score_pol + lower_and_upper_interval_limit2, 1, 0))

summarized_within_interval <-
   combined_result_measures_within_interval %>%
   group_by(risk_dist_transl, sample_size) %>%
   summarize(count_within_BC = sum(within_BC),
      count_within_pol = sum(within_pol),
      perc_within_BC = sum(within_BC) / replications_per_setting * 100,
      perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
   pivot_longer(cols = starts_with("perc"),
      names_to = "measure",
      values_to = "percentage",
      names_prefix = "perc_within_")

summarized_within_interval %>% ggplot(aes(factor(sample_size), percentage, col = risk_dist_transl, group = risk_dist_transl))+
   geom_point()+
   geom_line()+
   facet_wrap(~measure)+
   theme_minimal()+
   scale_y_continuous(breaks = seq(0, 100, by = 10))+
   labs(y = "percentage of simulations within 0.025 interval of true score",
        x = "sample size",
        title = "Interval of +- 0.025")+
   scale_color_manual(values = colors)+
   theme(legend.position = "none")

Difference between two polarizations

The approach above would only tell us how likely it is that the sampled distribution would lead to almost the same polarization value if one had sampled 5’000 people (or in a sense, a “true score”, as it converges to said population distribution). In the following section, we will go one step further and analyse how likely it is to find the same difference between two population distributions and two sampled distributions.

true_score_long <- true_score %>% 
   pivot_longer(cols = starts_with("mean_true_score"),
                names_prefix = "mean_true_score_",
                names_to = "measure",
                values_to = "value")


true_score_expanded <- expand.grid(x = true_score$risk_dist_transl, y =true_score$risk_dist_transl) %>% 
   filter(x != y) %>% 
   distinct() %>% 
   left_join(true_score, by = c("x" = "risk_dist_transl")) %>% 
   left_join(true_score, by = c("y" = "risk_dist_transl"), suffix = c("_x", "_y")) %>% 
   mutate(true_diff_BC = mean_true_score_BC_x - mean_true_score_BC_y,
          true_diff_pol = mean_true_score_pol_x - mean_true_score_pol_y)


#because we have duplicate combinations (e.g. abs(x - y) == abs(y - x)), I'll sort them and take half of it (e.g. only the neccessary combinations)
n_unique <- nrow(true_score) * (nrow(true_score)-1) / 2

true_score_unique <- true_score_expanded %>% 
   arrange(desc(true_diff_BC)) %>% 
   slice_head(n = n_unique) %>% 
   select(x, y, true_diff_BC, true_diff_pol)



true_score_unique %>% 
   kable(digits = 2)
x y true_diff_BC true_diff_pol
Strong Pol. None 2 0.54 0.70
Strong Pol. None 0.50 0.68
Rare None 2 0.42 0.06
Rare None 0.38 0.04
Strong Pol. Rare 0.12 0.64
None None 2 0.04 0.02

These are the true score differences between two distributions if one were to average the difference from sampling 5000 ratings for each population.

In the following, we will calculate the difference between the calculated polarization measures from another distribution. Again, we will take two intervals: .1 and .05 (that is, the sampled difference is allowed to deviate by .05 or .025 from the true score on either side).

Interval of .1

combined_result_measures_selected <- combined_result_measures %>% 
   select(contains("result"), sample_size, risk_dist_transl) %>% 
   group_by(risk_dist_transl, sample_size) %>% 
   mutate(sample_ID = row_number())

diff_distributions <- true_score_unique %>% 
   left_join(combined_result_measures_selected, by = c("x" = "risk_dist_transl")) %>% 
   left_join(combined_result_measures_selected, by = c("y" = "risk_dist_transl", "sample_size", "sample_ID"), suffix = c("_x", "_y")) %>% 
   mutate(diff_BC = BC_result_x - BC_result_y,
          diff_pol = polarization_result_x - polarization_result_y)

within_interval_diff_distributions <- diff_distributions %>% 
   mutate(within_BC = if_else(true_diff_BC >= diff_BC - lower_and_upper_interval_limit &
                                 true_diff_BC <= diff_BC + lower_and_upper_interval_limit, 1, 0),
          within_pol = if_else(true_diff_pol >= diff_pol - lower_and_upper_interval_limit &
                                 true_diff_pol <= diff_pol + lower_and_upper_interval_limit, 1, 0),
          comparison = paste(x, y, sep = " - ")
   ) %>% 
   group_by(comparison, sample_size) %>% 
   summarize(perc_within_BC = sum(within_BC) / replications_per_setting * 100,
             perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>% 
   pivot_longer(cols = starts_with("perc"),
                names_to = "measure",
                values_to = "percentage",
                names_prefix = "perc_within_")
   

within_interval_diff_distributions %>% ggplot(aes(factor(sample_size), percentage, col = factor(comparison), group = factor(comparison)))+
   geom_point(size = 2)+
   geom_line(linewidth = 1.2)+
   facet_wrap(~measure)+
   theme_minimal()+
   scale_y_continuous(breaks = seq(0, 100, by = 10))+
   labs(y = "percentage of simulations within 0.05 interval of true score",
        x = "sample size",
        title = "Interval of +- 0.05",
        color = "Comparison between")+
   scale_color_manual(values = colors2)+
   theme(legend.spacing = unit(0.1, "cm"),
         legend.margin = margin(0,0,0,0),
         legend.box.margin = margin(0,0,0,0),
         legend.position = c(.5,.05),
         legend.direction = "horizontal")

Interval of .05

within_interval_diff_distributions <- diff_distributions %>% 
   mutate(within_BC = if_else(true_diff_BC >= diff_BC - lower_and_upper_interval_limit2 &
                                 true_diff_BC <= diff_BC + lower_and_upper_interval_limit2, 1, 0),
          within_pol = if_else(true_diff_pol >= diff_pol - lower_and_upper_interval_limit2 &
                                 true_diff_pol <= diff_pol + lower_and_upper_interval_limit2, 1, 0),
          comparison = paste(x, y, sep = " - ")
   ) %>% 
   group_by(comparison, sample_size) %>% 
   summarize(perc_within_BC = sum(within_BC) / replications_per_setting * 100,
             perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>% 
   pivot_longer(cols = starts_with("perc"),
                names_to = "measure",
                values_to = "percentage",
                names_prefix = "perc_within_")
   

within_interval_diff_distributions %>% ggplot(aes(factor(sample_size), percentage, col = factor(comparison), group = factor(comparison)))+
   geom_point(size = 2)+
   geom_line(linewidth = 1.2)+
   facet_wrap(~measure)+
   theme_minimal()+
   scale_y_continuous(breaks = seq(0, 100, by = 10))+
   labs(y = "percentage of simulations within 0.025 interval of true score",
        x = "sample size",
        title = "Interval of +- 0.025",
        color = "Comparison between")+
   scale_color_manual(values = colors2)+
   theme(legend.spacing = unit(0.1, "cm"),
         legend.margin = margin(0,0,0,0),
         legend.box.margin = margin(0,0,0,0),
         legend.position = c(.5,.05),
         legend.direction = "horizontal")

Discussion

As one has expected, the “needed” sample size increases the stricter we set the intervals which we would be satisfied with.

If one were to only look at the plots with the stricter interval:

  • On the side of bimodality coefficient, one could argue to sample around 1250 participants so that we have a good chance of around 80% to get a good estimate for rare distributions (so that in case we do sample from a rare distribution, in 80% of the time, the true bimodality coefficient is off by only a small amount). If one wants to make sure, even sampling 2000 participants would be nice to increase the chance to 90%, but maybe that is stretching the budget a little bit…

  • For the measure of polarization, estimating the true score value is hardest if one has a strongly polarized distribution. In this case, we have a 80% chance of getting it “right” if one would sample 1250 participants, or 90% for sampling 2000 respectively.

The numbers here are only suggestions, and are only based on precision, so that we can be sure that the results we get are reliable enough. In any case, these numbers are “only” worst case scenarios, as the other population distributions are converging earlier. We are also only looking at the stricter scenario…

When looking for the difference between two distributions, one need to sample 1500 or even 2750 people for 80% or 90% chance of being near the true score.

Limitations

  • Discussion was solely based on the stricter interval (e.g. the last plot), not on the lenient one with a broader interval.
  • As always, the simulation should be taken with a grain of salt, as these results are made with the assumption that the distribution is as “beautiful” as I made it in the introduction section…
  • Whether we can truly sample as random as we want to, or we get some subsamples is not of debate here. This work just assumes that the whole population has X distribution.
  • No comparison between different sample distributions were made (e.g. overlap coefficient), so we still have no idea how big the sample size needs to be if we want to discover a difference of X between two countries/ groups in case there is a difference.

Credits

R Packages Used

LS0tDQp0aXRsZTogIlJpc2sgUG9sYXJpemF0aW9uIFBvd2VyIEFuYWx5c2lzIHVzaW5nIERldmlhbmNlIg0KYXV0aG9yOiAiQW5keSBDYW8iDQpkYXRlOiAiMjAyNC0wNC0yMyINCm91dHB1dDogDQogICBodG1sX2RvY3VtZW50Og0KICAgICAgY3NzOiBzdHlsZXMuY3NzDQogICAgICB0b2M6IHRydWUNCiAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UNCiAgICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgICAgY29sbGFwc2VkOiB0cnVlDQogICAgICBzbW9vdGhfY29udHJvbGw6IGZhbHNlDQogICAgICBmaWcud2lkdGg6IDI2DQogICAgICBmaWcuaGVpZ2h0OiAyNg0KICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24gKyBNZXRob2RzDQoNCmBgYHtyIFNldHVwLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAjZGF0YSB3cmFuZ2xpbmcgYW5kIG90aGVyIHRvb2xzIGZvciBSDQpsaWJyYXJ5KGtuaXRyKSAjIHJlcG9ydCBnZW5lcmF0aW9uIGluIFINCmxpYnJhcnkocHN5Y2gpICNjYWxjdWxhdGUgc2tldyBhbmQga3VydG9zaXMgZm9yIEJDDQpsaWJyYXJ5KGFncm10KSAjZm9yIGFncmVlbWVudCBhbmQgcG9sYXJpemF0aW9uIGNhbGN1bGF0aW9uDQpsaWJyYXJ5KHZpc2RhdCkgI3Zpc3VhbGl6ZSBkYXRhZnJhbWVzIGluIHBsb3RzDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgI2Vhc3kgdG8gdXNlIGNvbG9yIHBhbGV0dGVzDQpsaWJyYXJ5KHJtYXJrZG93bikgI2ZvciB0aGUgcGFnZWRfdGFibGUgZnVuY3Rpb24NCmxpYnJhcnkoZG9QYXJhbGxlbCkgI3BhcmFsbGVsIGNvbXB1dGF0aW9uIHVzaW5nIG11bHRpcGxlIGNvcmVzDQpsaWJyYXJ5KGZvcmVhY2gpICMgZm9yIGVhY2ggZnVuY3Rpb24sIHNvIHRoZSBzaW11bGF0aW9uIGRvZXMgbm90IHRha2UgYWdlcw0KDQoNCmxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCA8LSAuMDUNCmxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdDIgPC0gLjAyNQ0KDQpjb2xvcnMgPC0gYnJld2VyLnBhbCg0LCAiRGFyazIiKQ0KY29sb3JzMiA8LSBicmV3ZXIucGFsKDYsICJTZXQyIikNCg0Kbl9zY2FsZV9vbl9saWtlcnQgPC0gYygxOjExKQ0KbWluX2xpa2VydCA8LSBtaW4obl9zY2FsZV9vbl9saWtlcnQpDQptYXhfbGlrZXJ0IDwtIG1heChuX3NjYWxlX29uX2xpa2VydCkNCg0Kc2FtcGxlX3NlcXVlbmNlIDwtIGMoMjAsMzAsNDAsNTAsIDYwLCA3MCwgODAsIDkwLCAxMDAsIDE1MCwgMjAwLHNlcSgyNTAsIDMwMDAsIGJ5ID0gMjUwKSwgNTAwMCkNCnNhbXBsZWQgPC0gZGF0YS5mcmFtZShOX3BhcnQgPSBzYW1wbGVfc2VxdWVuY2UpDQoNCnJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyA8LSA0MDANCg0Kbl9wb3B1bGF0aW9uIDwtIDEwXjQNCnByb3BfbWlub3JpdHkgPC0gLjA1DQoNCiNzZXQgc2VlZCBzbyBldmVyeSByYW5kb20gdGhpbmcgaGVyZSBpcyByZXByb2R1Y2libGUNCnNldC5zZWVkKDQyKQ0KDQojc2V0IGVjaG8gPSBGQUxTRSAoZS5nLiBkb250IHNob3cgY29kZSBpbiBvdXRwdXQpIGZvciBhbGwgY2h1bmtzLCBleGNlcHQgd2hlbiBleHBsaWNpdGx5IHRlbGxpbmcgb3RoZXJ3aXNlDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogICBlY2hvID0gVFJVRSwNCiAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgIG1lc3NhZ2UgPSBGQUxTRQ0KICAgKQ0KYGBgDQoNClRoaXMgaXMgdGhlIDNyZCB0aW1lIEknbSB0cnlpbmcgdG8gZXN0aW1hdGUgdGhlIG5lZWRlZCBzYW1wbGUgc2l6ZSBmb3Igb3VyIHN0dWR5LiBBdCB0aGlzIHBvaW50LCB5b3Ugc2hvdWxkIChob3BlZnVsbHkpIGtub3cgdGhlIGRyaWxsLi4uICAgDQpUaGlzIHRpbWUgd2Ugd2lsbCB0cnkgdG8gZXN0aW1hdGUgb3VyIG5lZWRlZCBOIHdpdGggYW5vdGhlciBzaW11bGF0aW9uIGFwcHJvYWNoIHVzaW5nIGRldmlhbmNlIHRvIGEgInRydWUgc2NvcmUiIHdoaWNoIHdlIHdpbGwgZ2V0IHdpdGggc2FtcGxpbmcgYSBsb3QuDQoNCldlIHVzZSB0aGlzIHNhbXBsZSBzZXF1ZW5jZTogIA0KYGBge3J9DQpzYW1wbGVfc2VxdWVuY2UNCmBgYA0KDQpBbmQgZm9yIGVhY2ggb2YgdGhlc2Ugc2FtcGxlIHNpemVzLCB3ZSBzaW11bGF0ZSBgYHIgcmVwbGljYXRpb25zX3Blcl9zZXR0aW5nYGAgdGltZXMgKHdoaWxlIGhpZ2hlciBudW1iZXJzIHdvdWxkIGxlYWQgdG8gYSBtb3JlIHJvYnVzdC8gcmVsaWFibGUgZmluZGluZywgSSBzdGlsbCBuZWVkIHRvIG1ha2Ugc3VyZSB0aGF0IGl0IGNhbiBzdGlsbCBiZSBjb21wdXRhYmxlKS4uLg0KDQpIZXJlIGFyZSBvdXIgYXNzdW1lZCBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbiwgd2hpY2ggd2Ugd2lsbCBzYW1wbGUgZnJvbSBhZ2FpbjogIA0KDQpgYGB7ciBHZW5lcmF0aW9uIG9mIFBvcHVsYXRpb24gRGlzdHJpYnV0aW9ufQ0KDQpub3JtYWwgPC0gcm91bmQocm5vcm0obl9wb3B1bGF0aW9uLCBtZWFuID0gOSxzZCA9IDEuMikpDQpub3JtYWwgPC0gcG1pbihwbWF4KG5vcm1hbCwgbWluX2xpa2VydCksIG1heF9saWtlcnQpDQoNCnN0cm9uZ19wb2wgPC0gcm91bmQocmJldGEobl9wb3B1bGF0aW9uLCBzaGFwZTEgPSAuMSwgc2hhcGUyID0gLjEpKjEwKSArMQ0Kc3Ryb25nX3BvbCA8LSBwbWluKHBtYXgoc3Ryb25nX3BvbCwgbWluX2xpa2VydCksIG1heF9saWtlcnQpDQoNCnJhcmUgPC0gYyhyb3VuZChybm9ybShuX3BvcHVsYXRpb24gKigxLSBwcm9wX21pbm9yaXR5KSwgbWVhbiA9IDIsc2QgPSAxLjMpKSwgDQogICAgICAgICAgIHJvdW5kKHJub3JtKG5fcG9wdWxhdGlvbiAqcHJvcF9taW5vcml0eSwgbWVhbiA9IDExLHNkID0gLjUpKSkNCnJhcmUgPC0gcG1pbihwbWF4KHJhcmUsIG1pbl9saWtlcnQpLCBtYXhfbGlrZXJ0KQ0KDQpub3JtYWxfMiA8LSByb3VuZChybm9ybShuX3BvcHVsYXRpb24sIG1lYW4gPSA4LHNkID0gMSkpDQpub3JtYWxfMiA8LSBwbWluKHBtYXgobm9ybWFsXzIsIG1pbl9saWtlcnQpLCBtYXhfbGlrZXJ0KQ0KDQoNClBvcF9kZiA8LSBkYXRhLmZyYW1lKGBOb3JtYWwgRGlzdHJpYnV0aW9uYD0gbm9ybWFsLA0KICAgICAgICAgICAgICAgICAgICAgYE5vcm1hbCBEaXN0cmlidXRpb24gMmAgPSBub3JtYWxfMiwNCiAgICAgICAgICAgICAgICAgICAgIGBSYXJlIFBvbGFyaXphdGlvbmAgPSByYXJlLA0KICAgICAgICAgICAgICAgICAgICAgYFN0cm9uZyBQb2xhcml6YXRpb25gID0gc3Ryb25nX3BvbCkNCg0KbmFtZXMoUG9wX2RmKSA8LSBjKCJOb25lIiwgIk5vbmUgMiIsICJSYXJlIiwgIlN0cm9uZyBQb2wuIikNCg0KDQoNClBvcF9kZl9wbG90IDwtIFBvcF9kZiAlPiUgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgdmFsdWVzX3RvID0gIlJhdGluZyIsIG5hbWVzX3RvID0gIkRpc3RyIikgJT4lIA0KICAgbXV0YXRlKERpc3RyID0gZmFjdG9yKERpc3RyLCBsZXZlbHMgPSBjKCJOb25lIiwgIk5vbmUgMiIsIlJhcmUiLCAiU3Ryb25nIFBvbC4iKSkpDQoNClBvcF9kZl9wbG90ICU+JSBnZ3Bsb3QoYWVzKFJhdGluZywgZmlsbCA9IERpc3RyKSkrDQogICBnZW9tX2Jhcih3aWR0aD0gLjc1KSsNCiAgIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoRGlzdHIpKSsNCiAgIHNjYWxlX3hfY29udGludW91cyhuLmJyZWFrcyA9IDExLCBleHBhbmQgPSBjKDAsMCkpKw0KICAgdGhlbWVfbWluaW1hbCgpKw0KICAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMiwgMCwgMCksDQogICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSsNCiAgIGdlb21fdGV4dChhZXMoeD02LHkgPSAzNTAwLCBsYWJlbD0gRGlzdHIsIGNvbG9yID0gRGlzdHIpLCBzaXplID0gNi44KSsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzKSsNCiAgIHlsYWIoIkNvdW50IikNCmBgYA0KDQpgYGB7ciBTYW1wbGluZywgY2FjaGU9VFJVRX0NCiMgbWF4X2NvbHMgPC0gbWF4KHNhbXBsZWQpDQojIA0KIyAjIFJlZ2lzdGVyIHBhcmFsbGVsIGJhY2tlbmQgd2l0aCB0aGUgZGVzaXJlZCBudW1iZXIgb2YgY29yZXMNCiMgbnVtX2NvcmVzIDwtIGRldGVjdENvcmVzKCktMQ0KIyANCiMgY2wgPC0gbWFrZUNsdXN0ZXIobnVtX2NvcmVzKQ0KIyByZWdpc3RlckRvUGFyYWxsZWwoY2wpDQojIA0KIyAjcm90YXRlIFBvcF9kZiwgc28gb3VyIGZ1bmN0aW9uIHdvcmtzIChlYWNoIHJvdyBuZWVkcyB0byBiZSBhIGRpZmZlcmVudCBkaXN0cmlidXRpb24sIGluc3RlYWQgb2YgZWFjaCBjb2wpDQojIHJvdF9Qb3BfZGYgPC0gYXMuZGF0YS5mcmFtZSh0KFBvcF9kZikpDQojIA0KIyByaXNrX2Rpc3RfdHIgPC0gZGF0YS5mcmFtZShyaXNrX2Rpc3RyaWJ1dGlvbiA9IDE6NCwNCiMgICAgICAgICAgICByaXNrX2Rpc3RfdHJhbnNsID0gcm93bmFtZXMocm90X1BvcF9kZikpDQojIA0KIyAjIERlZmluZSBmdW5jdGlvbiB0byBwcm9jZXNzIGVhY2ggY29tYmluYXRpb24gb2Ygcmlza19kaXN0cmlidXRpb24sIHNhbXBsZXNpemUgYW5kIHJlcGxpY2F0aW9ucyBwZXIgc2V0dGluZw0KIyBzYW1wbGVfYW5kX3JlcGxpY2F0ZV9mb3JfYWxsX3Jpc2tzIDwtIGZ1bmN0aW9uKGkpIHsNCiMgICBzYW1wbGVkX21hdHJpeF9saXN0IDwtIGxpc3QoKQ0KIyANCiMgICBmb3IgKGogaW4gMTpucm93KHJvdF9Qb3BfZGYpKSB7DQojICAgICBtYXQgPC0gcmVwbGljYXRlKHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZywNCiMgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlKDE6bl9wb3B1bGF0aW9uLCBzaXplID0gc2FtcGxlZFtpLCAxXSwgcmVwbGFjZSA9IFRSVUUpKSAjY3JlYXRlIG1hdHJpeCBvZiBvdXIgc2FtcGxlcyB3aXRoIHJlcGxhY2luZywgdGltZXMgbiAtIHJlcGxpY2F0aW9ucw0KIyANCiMgICAgIHNhbXBsZWRfdGFibGUgPC0gcm90X1BvcF9kZltqLCBtYXRdICN1c2luZyB0aGUgbWF0cml4LCBjb2xsZWN0IHRoZSB2YWx1ZXMgZnJvbSBvdXIgcmlzayBkaXN0cmlidXRpb24gbWF0cml4IChhcyBhIHZlY3RvciB0aG91Z2guLi4pDQojICAgICBzYW1wbGVkX21hdHJpeCA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChzYW1wbGVkX3RhYmxlLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBzYW1wbGVkW2ksIDFdLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnlyb3cgPSBUUlVFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBOVUxMKSkgI2NyZWF0ZSBkZiBvdXQgb2YgdGhlc2UgdmVjdG9ycyBpbnN0ZWFkIG9mIGZsYXQgdmVjdG9ycw0KIyANCiMgDQojICAgICBpZiAobmNvbChzYW1wbGVkX21hdHJpeCkgPCBtYXhfY29scykgew0KIyAgICAgICBwYWRkaW5nX21hdHJpeCA8LSBtYXRyaXgoTkEsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbnJvdyhzYW1wbGVkX21hdHJpeCksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbWF4X2NvbHMgLSBuY29sKHNhbXBsZWRfbWF0cml4KSkNCiMgICAgICAgc2FtcGxlZF9tYXRyaXggPC0gY2JpbmQoc2FtcGxlZF9tYXRyaXgsIHBhZGRpbmdfbWF0cml4KQ0KIyAgICAgfSAjaWYgbWF0cml4IGlzIG5vdCB3aWRlIGVub3VnaCBmb3Igb3VyIGVuZCByZXN1bHQgbWF0cml4LCBwYWRkIGl0IHdpdGggTkEgY29sdW1ucywgc28gYmluZGluZyByb3dzIGlzIGRvYWJsZSAobmVlZHMgc2FtZSBhbW91bnQgb2YgbmNvbHMpDQojIA0KIyAgICAgc2FtcGxlZF9tYXRyaXggPC0gY2JpbmQobWF0cml4KHNhbXBsZWRbaSwxXSwgbnJvdyA9IHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRyaXgoaiwgbnJvdyA9IHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVkX21hdHJpeCkgI2JpbmQgY29sdW1ucyB3aXRoIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gc3VjaCBhcyBzYW1wbGUgc2l6ZSBhbmQgd2hpY2ggcmlza19kaXN0cmlidXRpb24gd2FzIHNhbXBsZWQNCiMgDQojICAgICBjb2xuYW1lcyhzYW1wbGVkX21hdHJpeCkgPC0gYygic2FtcGxlX3NpemUiLCAicmlza19kaXN0cmlidXRpb24iLCBwYXN0ZTAoInJhdGluZ18iLCAxOm1heF9jb2xzKSkgI3Jld3JpdGUgY29sbmFtZXMgc28gaXQgaXMgaWRlbnRpY2FsIHRvIHRoZSBiaWdoIG1hdHJpeA0KIyAgICAgc2FtcGxlZF9tYXRyaXhfbGlzdFtbal1dIDwtIHNhbXBsZWRfbWF0cml4ICNzdG9yZSBpbiBsaXN0DQojICAgfQ0KIyAgIHJldHVybihkby5jYWxsKHJiaW5kLCBzYW1wbGVkX21hdHJpeF9saXN0KSkgI2FmdGVyIGFsbCByaXNrIGRpc3RyaWJ1dGlvbnMgYXJlIHNhbXBsZWQgZnJvbSwgYmluZCB0aGVtIGFsbCBhbmQgcmV0dXJuIHRoZSBvdXRwdXQNCiMgDQojIH0NCiMgDQojICMgUGVyZm9ybSBwYXJhbGxlbCBwcm9jZXNzaW5nIHVzaW5nIGZvcmVhY2gsIGl0ZXJhdGluZyB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgc2FtcGxlc2l6ZXMNCiMgcmVzdWx0IDwtIGZvcmVhY2goaSA9IDE6bnJvdyhzYW1wbGVkKSwgLmNvbWJpbmUgPSByYmluZCkgJWRvcGFyJSB7DQojICAgc2FtcGxlX2FuZF9yZXBsaWNhdGVfZm9yX2FsbF9yaXNrcyhpKQ0KIyB9DQojIA0KIyAjIFN0b3AgdGhlIHBhcmFsbGVsIGJhY2tlbmQNCiMgc3RvcENsdXN0ZXIoY2wpDQojIA0KIyByZXN1bHQgPC0gbXV0YXRlX2FsbChyZXN1bHQsIGFzLm51bWVyaWMpDQojIA0KIyANCiMgdmlzX21pc3MocmVzdWx0WyxzZXEoZnJvbSA9MywgdG8gPSBuY29sKHJlc3VsdCksIGxlbmd0aC5vdXQgPTIwKV0sIHNob3dfcGVyY19jb2wgPSBGKQ0KYGBgICANCg0KDQpgYGB7ciBDYWxjdWxhdGluZyBQb2xhcmlzYXRpb24gTWVhc3VyZXMsIGNhY2hlPVRSVUV9DQojdGFrZW4gZnJvbSBteSBwcmV2aW91cyB3b3JrDQoNCiMgY2FsY19iaW1vZGFsaXR5X2NvZWZmaWNpZW50IDwtIGZ1bmN0aW9uKHZlYyl7DQojICAgIHNrZXcgPC0gc2tldyh2ZWMsIG5hLnJtID0gVFJVRSwgdHlwZSA9IDMpDQojICAgIGt1cnRvc2lzIDwtIGt1cnRvc2kodmVjLCBuYS5ybSA9IFRSVUUsIHR5cGUgPSAzKQ0KIyAgICBuIDwtIHN1bSghaXMubmEodmVjKSkNCiMgICAgcmV0dXJuKChza2V3XjIrMSkgLyAoa3VydG9zaXMgKyAoKDMqKChuLTEpKV4yKS8oKG4tMikqKG4tMykpKSApKQ0KIyB9DQojIA0KIyAjIENyZWF0ZWQgdGhpcyBvbmUgImZyb20gc2NyYXRjaCIsIGFzIHdlIHdvcmsgd2l0aCBhIHNtYWxsZXIgc2NhbGUgb2YgMTEgaW5zdGVhZCBvZiAxMDEsIHRoZSBjYWxjdWxhdGlvbiBvZiBwb2xhcml6YXRpb24gZG9lcyBub3QgdGFrZSB0b28gbG9uZyBhbnltb3JlDQojIA0KIyBjYWxjX3BvbGFyaXphdGlvbiA8LSBmdW5jdGlvbih2ZWMpew0KIyAgICB2ZWMyIDwtIGFzLnZlY3Rvcih2ZWMpDQojICAgIGZyZXFfdmVjIDwtIGFncm10Ojpjb2xsYXBzZSh2ZWMyLCBwb3MgPSBjKDE6MTEpKQ0KIyAgICByZXR1cm4oYWdybXQ6OnBvbGFyaXphdGlvbihmcmVxX3ZlYykpDQojIH0NCiMgDQojICMgdGhpcyBjb2RlIGlzIGNvbW1lbnRlZCwgYXMgdGhlIGNvbXB1dGF0aW9uIHRha2VzIHRvbyBsb25nLCBhbmQgSSBoYXRlIHdhaXRpbmcgd2hpbGUga25pdGluZy4gSSBoYXZlIHNhdmVkIGEgc2VwcGFyYXRlIHJkcyBmaWxlLCB3aGljaCB3aWxsIGJlIHJlYWQgaW4gaW5zdGVhZC4gUmVhZGVycyB3aG8gd2FudCB0byB1bmNvbW1lbnQgc2VjdGlvbiwgc2VsZWN0IHRoZSBsaW5lcyB0byB1bmNvbW1lbnQgYW5kIHByZXNzIEN0cmwgKyBTaGlmdCArIEMgKG9uIFdpbmRvd3MvTGludXgpIG9yIENtZCArIFNoaWZ0ICsgQyAob24gbWFjT1MpDQojICMNCiMgIyAjYXBwbHkgdGhlIGZ1bmN0aW9ucyB0byBvdXIgcmVzdWx0IG1hdHJpeCwgdGhlcmVmb3JlIGNhbGN1bGF0ZSBmb3IgZWFjaCBkcmF3biBzYW1wbGUgdGhlIHBvbGFyaXphdGlvbiBtZXRyaWNzDQojIEJDX3Jlc3VsdCA8LSBhcHBseShyZXN1bHRbLC1jKDE6MildLCAxLCBjYWxjX2JpbW9kYWxpdHlfY29lZmZpY2llbnQpDQojIHN1bShpcy5uYShCQ19yZXN1bHQpKQ0KIyANCiMgDQojICMgUmVnaXN0ZXIgcGFyYWxsZWwgYmFja2VuZCB3aXRoIHRoZSBkZXNpcmVkIG51bWJlciBvZiBjb3Jlcw0KIyBudW1fY29yZXMgPC0gZGV0ZWN0Q29yZXMoKS0xDQojIA0KIyBjbCA8LSBtYWtlQ2x1c3RlcihudW1fY29yZXMpDQojIHJlZ2lzdGVyRG9QYXJhbGxlbChjbCkNCiMgDQojICMgUGVyZm9ybSBwYXJhbGxlbCBwcm9jZXNzaW5nIHVzaW5nIGZvcmVhY2gsIGl0ZXJhdGluZyB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgZHJhd24gc2FtcGxlcw0KIyBwb2xhcml6YXRpb25fcmVzdWx0IDwtIGZvcmVhY2goaSA9IDE6bnJvdyhyZXN1bHQpLCAuY29tYmluZSA9IHJiaW5kKSAlZG9wYXIlIHsNCiMgICBjYWxjX3BvbGFyaXphdGlvbihyZXN1bHRbaSwgLWMoMToyKV0pDQojIH0NCiMgc3VtKGlzLm5hKHBvbGFyaXphdGlvbl9yZXN1bHQpKQ0KIyANCiMgIyBTdG9wIHRoZSBwYXJhbGxlbCBiYWNrZW5kDQojIHN0b3BDbHVzdGVyKGNsKQ0KIyANCiMgDQojIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyA8LSBjYmluZChwb2xhcml6YXRpb25fcmVzdWx0LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQkNfcmVzdWx0LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0WywxOjJdDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQojIHN1bShpcy5uYShjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMpKQ0KIyANCiMgDQojIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lDQojICAgIGxlZnRfam9pbihyaXNrX2Rpc3RfdHIsIGJ5ID0gInJpc2tfZGlzdHJpYnV0aW9uIikgJT4lDQojICAgIG11dGF0ZShmc2FtcGxlX3NpemUgPSBmYWN0b3Ioc2FtcGxlX3NpemUsIG9yZGVyZWQgPSBUKSkNCiMgDQojIHNhdmVSRFMoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzLCAic2F2ZWRfUkRTXFxjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXNfcG93ZXJfYW5hbHlzaXNfZGV2aWFuY2UucmRzIikNCg0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzIDwtIHJlYWRSRFMoInNhdmVkX1JEU1xcY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3Bvd2VyX2FuYWx5c2lzX2RldmlhbmNlLnJkcyIpDQoNCg0KDQp2aXNfbWlzcyhjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMsIHNvcnRfbWlzcyA9IEYpDQpgYGANCg0KIyBSZXN1bHRzDQoNCkluIHRoZSBmb2xsb3dpbmcgZGVuc2l0eSBwbG90cywgd2Ugc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCBjYWxjdWxhdGVkICoqcG9sYXJpemF0aW9uKiogbWVhc3VyZSAoZnJvbSB0aGUgYm9vayAiVHJpZ2dlcnB1bmt0ZSIgZnJvbSBNYXUgZXQgYWwuKSwgd2hpY2ggcmFuZ2VzIGZyb20gMCAobm90IHBvbGFyaXplZCwgcGVvcGxlIGFyZSBpbiBhZ3JlZW1lbnQpIHRvIDEgKGhpZ2hlc3QgcG9sYXJpemF0aW9uKS4gV2UgY2FuIHNlZSB0aGF0IHRoZSBtb3JlIHdlIHNhbXBsZSwgdGhlIG1vcmUgdGhlIHJlc3VsdHMgY29udmVyZ2UgdG8gYSAidHJ1ZSIgc2NvcmUgZm9yIHNhaWQgYXNzdW1lZCBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3J9DQpjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgZ2dwbG90KGFlcyhwb2xhcml6YXRpb25fcmVzdWx0KSkrDQogICBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBmc2FtcGxlX3NpemUpLCBhbHBoYSA9IC40KSsNCiAgIGZhY2V0X2dyaWQocm93cyA9dmFycyhyaXNrX2Rpc3RfdHJhbnNsKSkNCmBgYA0KDQpBbmQgZm9yIHRoZSBiaW1vZGFsaXR5IENvZWZmaWNpZW50IGFzIHdlbGw6DQoNCmBgYHtyfQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGdncGxvdChhZXMoQkNfcmVzdWx0KSkrDQogICBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBmc2FtcGxlX3NpemUpLCBhbHBoYSA9IC40KSsNCiAgIGZhY2V0X2dyaWQocm93cyA9dmFycyhyaXNrX2Rpc3RfdHJhbnNsKSkNCmBgYA0KDQpJbiB0aGUgZm9sbG93aW5nLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgKiptb2RlKiogb2YgZWFjaCBkZW5zaXR5IHBsb3QgKHRoYXQgaXMsIHdoZXJlIHRoZSBwb2xhcml6YXRpb24gbWVhc3VyZXMgY29udmVyZ2VzIG9yIGhhcyBhIGhpZ2ggcHJvYmFiaWxpdHkgdGhhdCBpdCBlbmRzIHVwIG9uIFgsICoqaWYgd2Ugd2VyZSB0byBzYW1wbGUgaXQgZmV3ZXIgdGltZXMqKikuIFdlIGNhbiB0aGVuIHBsb3QgdGhlc2UgZmluZGluZ3MsIGFuZCBzaG91bGQgbG9vayBhdCB0aGUgcG9pbnQgd2hlcmUgdGhlICoqbW9kZXMqKiBjb252ZXJnZXMgdG93YXJkcyB0aGUgInRydWUgc2NvcmUiIChpbiBvdXIgY2FzZSwgdGhlIHNhbXBsZSBzaXplIG9mIDUwMDApLg0KDQpgYGB7ciwgZmlnLndpZHRoPTE0fQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzIDwtIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyAlPiUgDQogICBtdXRhdGUoZ3JvdXBlZF9kaXN0WHNhbXBsZSA9IHBhc3RlKHJpc2tfZGlzdF90cmFuc2wsIGZzYW1wbGVfc2l6ZSwgc2VwID0gIl8iKSkNCg0Kc3BsaXR0ZWQgPC0gc3BsaXQoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzJHBvbGFyaXphdGlvbl9yZXN1bHQsIGYgPSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMkZ3JvdXBlZF9kaXN0WHNhbXBsZSkNCnNwbGl0dGVkMiA8LSBzcGxpdChjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMkQkNfcmVzdWx0LCBmID0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzJGdyb3VwZWRfZGlzdFhzYW1wbGUpDQoNCm1vZGVfZGVuc19yZXMgPC0gZGF0YS5mcmFtZSgpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzcGxpdHRlZCkpIHsNCg0KICAgZGVuc19yZXMgPC0gZGVuc2l0eShzcGxpdHRlZFtbaV1dKQ0KICAgZGVuc19yZXMyIDwtIGRlbnNpdHkoc3BsaXR0ZWQyW1tpXV0pDQogICANCiAgIGRlbnNfZGYgPC0gZGF0YS5mcmFtZShncm91cCA9IG5hbWVzKHNwbGl0dGVkKVtpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlX2RlbnNfcG9sID0gZGVuc19yZXMkeFt3aGljaC5tYXgoZGVuc19yZXMkeSldLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVfZGVuc19CQyA9IGRlbnNfcmVzMiR4W3doaWNoLm1heChkZW5zX3JlczIkeSldKQ0KICAgDQogICBtb2RlX2RlbnNfcmVzIDwtIHJiaW5kKG1vZGVfZGVuc19yZXMsIGRlbnNfZGYpDQogICANCn0NCg0KY2xlYW5lZF9tb2RlX2RlbnNfcmVzIDwtIG1vZGVfZGVuc19yZXMgJT4lIA0KICAgc2VwYXJhdGUoZ3JvdXAsIHNlcCA9ICJfIiwgaW50byA9IGMoIkRpc3RyaWJ1dGlvbiIsICJmc2FtcGxlX3NpemUiKSkgJT4lIA0KICAgbXV0YXRlKFNhbXBsZV9TaXplID0gYXMuaW50ZWdlcihmc2FtcGxlX3NpemUpKSAlPiUgDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJtb2RlX2RlbnMiKSwNCiAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAibW9kZV9kZW5zXyIsDQogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsDQogICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikNCiAgIA0KDQpjbGVhbmVkX21vZGVfZGVuc19yZXMgJT4lIA0KICAgZ2dwbG90KGFlcyhTYW1wbGVfU2l6ZSwgdmFsdWUsIGdyb3VwID0gRGlzdHJpYnV0aW9uLCBjb2wgPSBEaXN0cmlidXRpb24pKSsNCiAgIGdlb21fbGluZShzaXplPSAxLjEpKw0KICAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhtZWFzdXJlKSkrDQogICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2FtcGxlX3NlcXVlbmNlKSsNCiAgIHRoZW1lX21pbmltYWwoKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpDQoNCmBgYA0KDQojIyBQcm9iYWJpbGl0eSBvZiBTYW1wbGUgYmVpbmcgaW4gVHJ1ZSBTY29yZSBJbnRlcnZhbCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpXaGlsZSB0aGUgbW9kZSBjb3VsZCBhIGJlIGdvb2QgaW5kaWNhdG9yLCBvbmUgbWlnaHQgYWxzbyBhcmd1ZSB0aGF0IGl0IGlzIG1vcmUgcmVsZXZhbnQgdG8gaGF2ZSBhIGdvb2QgY2hhbmNlIG9mIGNhcHR1cmluZyB0aGUgdHJ1ZSBwb2xhcml6YXRpb24gdmFsdWUuIFRoYXQgaXMsIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBtZWFzdXJlIGlzIG9ubHkgb2ZmIGJ5IG9ubHkgLjEgKG1lYW5pbmcgaXQgaXMgYWx3YXlzIHdpdGhpbiBhIGludGVydmFsIG9mICsgMC4wNSBhbmQgLSAwLjA1KSBmcm9tIHRoZSB0cnVlIHNjb3JlLiBTbyBsZXQncyBjYWxjdWxhdGUgdGhlICUgd2hlcmUgdGhlIGBgciByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmdgYCBzaW11bGF0aW9ucyBwZXIgc2FtcGxlIHNpemUgd2FzIHdpdGhpbiB0aGlzIGludGVydmFsLiBPbmUgY291bGQgdGhlbiBpbnRlcnByZXQgdGhlIHJlc3VsdHMgYXM6ICoqdGhlIHByb2JhYmlsaXR5IG9mIGNvbWluZyB0byB0aGUgc2FtZSBwb2xhcml6YXRpb24gdmFsdWUgKHdpdGggYW4gaW50ZXJ2YWwgb2YgLjEpIGNvbXBhcmVkIHRvIGlmIHdlIGhhZCBzYW1wbGVkIDUnMDAwIHBlb3BsZSoqLg0KDQojIyMgSW50ZXJ2YWwgb2YgLjENCg0KYGBge3IsIGZpZy53aWR0aD0gMTR9DQojIGNhbGN1bGF0ZSB0aGUgInRydWUgc2NvcmUiIHZhbHVlIG9mIHNhaWQgcG9sYXJpemF0aW9uIG1lYXN1cmUgZm9yIGVhY2ggcG9wdWxhdGlvbiBkaXN0cmlidXRpb24NCg0KdHJ1ZV9zY29yZSA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgZmlsdGVyKHNhbXBsZV9zaXplID09IDUwMDApICU+JSANCiAgIGdyb3VwX2J5KHJpc2tfZGlzdF90cmFuc2wpICU+JSANCiAgIHN1bW1hcml6ZShtZWFuX3RydWVfc2NvcmVfQkMgPSBtZWFuKEJDX3Jlc3VsdCksDQogICAgICAgICAgICAgbWVhbl90cnVlX3Njb3JlX3BvbCA9IG1lYW4ocG9sYXJpemF0aW9uX3Jlc3VsdCkpDQoNCg0KDQoNCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgPC0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGxlZnRfam9pbih0cnVlX3Njb3JlLCBieSA9IGMoInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbXV0YXRlKHdpdGhpbl9CQyA9IGlmX2Vsc2UoQkNfcmVzdWx0ID49IG1lYW5fdHJ1ZV9zY29yZV9CQy0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0ICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCQ19yZXN1bHQgPD0gbWVhbl90cnVlX3Njb3JlX0JDICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSwNCiAgICAgICAgICB3aXRoaW5fcG9sID0gaWZfZWxzZShwb2xhcml6YXRpb25fcmVzdWx0ID49IG1lYW5fdHJ1ZV9zY29yZV9wb2wtIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9sYXJpemF0aW9uX3Jlc3VsdCA8PSBtZWFuX3RydWVfc2NvcmVfcG9sICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSkNCg0Kc3VtbWFyaXplZF93aXRoaW5faW50ZXJ2YWwgPC0NCiAgIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgJT4lDQogICBncm91cF9ieShyaXNrX2Rpc3RfdHJhbnNsLCBzYW1wbGVfc2l6ZSkgJT4lDQogICBzdW1tYXJpemUoY291bnRfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQyksDQogICAgICBjb3VudF93aXRoaW5fcG9sID0gc3VtKHdpdGhpbl9wb2wpLA0KICAgICAgcGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgIHBlcmNfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCkgJT4lDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJwZXJjIiksDQogICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgIHZhbHVlc190byA9ICJwZXJjZW50YWdlIiwNCiAgICAgIG5hbWVzX3ByZWZpeCA9ICJwZXJjX3dpdGhpbl8iKQ0KDQpzdW1tYXJpemVkX3dpdGhpbl9pbnRlcnZhbCAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSByaXNrX2Rpc3RfdHJhbnNsLCBncm91cCA9IHJpc2tfZGlzdF90cmFuc2wpKSsNCiAgIGdlb21fcG9pbnQoKSsNCiAgIGdlb21fbGluZSgpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDUgaW50ZXJ2YWwgb2YgdHJ1ZSBzY29yZSIsDQogICAgICAgIHggPSAic2FtcGxlIHNpemUiLA0KICAgICAgICB0aXRsZSA9ICJJbnRlcnZhbCBvZiArLSAwLjA1IikrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzKSsNCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCiAgIA0KDQpgYGANCg0KIyMjIEludGVydmFsIG9mIC4wNSB7LmFjdGl2ZX0NCg0KQXMgdGhlIGludGVydmFsIG9mIC4wNSAod2hpY2ggcmVzdWx0cyBvZiBhIGNvdmVyYWdlIG9mIC4xKSB3YXMgImFyYml0cmFyaWx5IiB0YWtlbiwgbGV0J3MgdXNlIHRoZSBzYW1lIHByb2NlZHVyZSBmb3IgYW4gaW50ZXJ2YWwgb2YgLjAyNSAob3IgYSBzcGFuIG9mIG9ubHkgMC4wNSkuIEluIHRoaXMgY2FzZSwgb25lIHdvdWxkIGV4cGVjdCB0aGF0IHdlIG5lZWQgYSBiaWdnZXIgc2FtcGxlIHNpemUsIGFzIHRoZSBtYXJnaW4gZm9yICJlcnJvciIgaXMgc21hbGxlciB3aGVyZSB3ZSB3b3VsZCBiZSBjb25maWRlbnQgZW5vdWdoIHRoYXQgd2UgaGF2ZSBjYXB0dXJlZCB0aGUgdHJ1ZSBzY29yZS4NCg0KDQpgYGB7ciwgZmlnLndpZHRoPTE0fQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3dpdGhpbl9pbnRlcnZhbCA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgbGVmdF9qb2luKHRydWVfc2NvcmUsIGJ5ID0gYygicmlza19kaXN0X3RyYW5zbCIpKSAlPiUgDQogICBtdXRhdGUod2l0aGluX0JDID0gaWZfZWxzZShCQ19yZXN1bHQgPj0gbWVhbl90cnVlX3Njb3JlX0JDLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCQ19yZXN1bHQgPD0gbWVhbl90cnVlX3Njb3JlX0JDICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiwgMSwgMCksDQogICAgICAgICAgd2l0aGluX3BvbCA9IGlmX2Vsc2UocG9sYXJpemF0aW9uX3Jlc3VsdCA+PSBtZWFuX3RydWVfc2NvcmVfcG9sLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb2xhcml6YXRpb25fcmVzdWx0IDw9IG1lYW5fdHJ1ZV9zY29yZV9wb2wgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyLCAxLCAwKSkNCg0Kc3VtbWFyaXplZF93aXRoaW5faW50ZXJ2YWwgPC0NCiAgIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgJT4lDQogICBncm91cF9ieShyaXNrX2Rpc3RfdHJhbnNsLCBzYW1wbGVfc2l6ZSkgJT4lDQogICBzdW1tYXJpemUoY291bnRfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQyksDQogICAgICBjb3VudF93aXRoaW5fcG9sID0gc3VtKHdpdGhpbl9wb2wpLA0KICAgICAgcGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgIHBlcmNfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCkgJT4lDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJwZXJjIiksDQogICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgIHZhbHVlc190byA9ICJwZXJjZW50YWdlIiwNCiAgICAgIG5hbWVzX3ByZWZpeCA9ICJwZXJjX3dpdGhpbl8iKQ0KDQpzdW1tYXJpemVkX3dpdGhpbl9pbnRlcnZhbCAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSByaXNrX2Rpc3RfdHJhbnNsLCBncm91cCA9IHJpc2tfZGlzdF90cmFuc2wpKSsNCiAgIGdlb21fcG9pbnQoKSsNCiAgIGdlb21fbGluZSgpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDI1IGludGVydmFsIG9mIHRydWUgc2NvcmUiLA0KICAgICAgICB4ID0gInNhbXBsZSBzaXplIiwNCiAgICAgICAgdGl0bGUgPSAiSW50ZXJ2YWwgb2YgKy0gMC4wMjUiKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpKw0KICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KIyMgRGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBwb2xhcml6YXRpb25zIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClRoZSBhcHByb2FjaCBhYm92ZSB3b3VsZCBvbmx5IHRlbGwgdXMgaG93IGxpa2VseSBpdCBpcyB0aGF0IHRoZSBzYW1wbGVkIGRpc3RyaWJ1dGlvbiB3b3VsZCBsZWFkIHRvIGFsbW9zdCB0aGUgc2FtZSBwb2xhcml6YXRpb24gdmFsdWUgaWYgb25lIGhhZCBzYW1wbGVkIDUnMDAwIHBlb3BsZSAob3IgaW4gYSBzZW5zZSwgYSAidHJ1ZSBzY29yZSIsIGFzIGl0IGNvbnZlcmdlcyB0byBzYWlkIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9uKS4gSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLCB3ZSB3aWxsIGdvIG9uZSBzdGVwIGZ1cnRoZXIgYW5kIGFuYWx5c2UgaG93IGxpa2VseSBpdCBpcyB0byBmaW5kIHRoZSBzYW1lIGRpZmZlcmVuY2UgYmV0d2VlbiAqKnR3byoqIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9ucyBhbmQgdHdvIHNhbXBsZWQgZGlzdHJpYnV0aW9ucy4gIA0KDQpgYGB7cn0NCnRydWVfc2NvcmVfbG9uZyA8LSB0cnVlX3Njb3JlICU+JSANCiAgIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIm1lYW5fdHJ1ZV9zY29yZSIpLA0KICAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJtZWFuX3RydWVfc2NvcmVfIiwNCiAgICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKQ0KDQoNCnRydWVfc2NvcmVfZXhwYW5kZWQgPC0gZXhwYW5kLmdyaWQoeCA9IHRydWVfc2NvcmUkcmlza19kaXN0X3RyYW5zbCwgeSA9dHJ1ZV9zY29yZSRyaXNrX2Rpc3RfdHJhbnNsKSAlPiUgDQogICBmaWx0ZXIoeCAhPSB5KSAlPiUgDQogICBkaXN0aW5jdCgpICU+JSANCiAgIGxlZnRfam9pbih0cnVlX3Njb3JlLCBieSA9IGMoIngiID0gInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbGVmdF9qb2luKHRydWVfc2NvcmUsIGJ5ID0gYygieSIgPSAicmlza19kaXN0X3RyYW5zbCIpLCBzdWZmaXggPSBjKCJfeCIsICJfeSIpKSAlPiUgDQogICBtdXRhdGUodHJ1ZV9kaWZmX0JDID0gbWVhbl90cnVlX3Njb3JlX0JDX3ggLSBtZWFuX3RydWVfc2NvcmVfQkNfeSwNCiAgICAgICAgICB0cnVlX2RpZmZfcG9sID0gbWVhbl90cnVlX3Njb3JlX3BvbF94IC0gbWVhbl90cnVlX3Njb3JlX3BvbF95KQ0KDQoNCiNiZWNhdXNlIHdlIGhhdmUgZHVwbGljYXRlIGNvbWJpbmF0aW9ucyAoZS5nLiBhYnMoeCAtIHkpID09IGFicyh5IC0geCkpLCBJJ2xsIHNvcnQgdGhlbSBhbmQgdGFrZSBoYWxmIG9mIGl0IChlLmcuIG9ubHkgdGhlIG5lY2Nlc3NhcnkgY29tYmluYXRpb25zKQ0Kbl91bmlxdWUgPC0gbnJvdyh0cnVlX3Njb3JlKSAqIChucm93KHRydWVfc2NvcmUpLTEpIC8gMg0KDQp0cnVlX3Njb3JlX3VuaXF1ZSA8LSB0cnVlX3Njb3JlX2V4cGFuZGVkICU+JSANCiAgIGFycmFuZ2UoZGVzYyh0cnVlX2RpZmZfQkMpKSAlPiUgDQogICBzbGljZV9oZWFkKG4gPSBuX3VuaXF1ZSkgJT4lIA0KICAgc2VsZWN0KHgsIHksIHRydWVfZGlmZl9CQywgdHJ1ZV9kaWZmX3BvbCkNCg0KDQoNCnRydWVfc2NvcmVfdW5pcXVlICU+JSANCiAgIGthYmxlKGRpZ2l0cyA9IDIpDQpgYGANCg0KVGhlc2UgYXJlIHRoZSB0cnVlIHNjb3JlIGRpZmZlcmVuY2VzIGJldHdlZW4gdHdvIGRpc3RyaWJ1dGlvbnMgaWYgb25lIHdlcmUgdG8gYXZlcmFnZSB0aGUgZGlmZmVyZW5jZSBmcm9tIHNhbXBsaW5nIDUwMDAgcmF0aW5ncyBmb3IgZWFjaCBwb3B1bGF0aW9uLiAgDQoNCg0KSW4gdGhlIGZvbGxvd2luZywgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY2FsY3VsYXRlZCBwb2xhcml6YXRpb24gbWVhc3VyZXMgZnJvbSBhbm90aGVyIGRpc3RyaWJ1dGlvbi4gQWdhaW4sIHdlIHdpbGwgdGFrZSB0d28gaW50ZXJ2YWxzOg0KLjEgYW5kIC4wNSAodGhhdCBpcywgdGhlIHNhbXBsZWQgZGlmZmVyZW5jZSBpcyBhbGxvd2VkIHRvIGRldmlhdGUgYnkgLjA1IG9yIC4wMjUgZnJvbSB0aGUgdHJ1ZSBzY29yZSBvbiBlaXRoZXIgc2lkZSkuDQoNCiMjIyBJbnRlcnZhbCBvZiAuMQ0KDQpgYGB7ciwgZmlnLndpZHRoPSAxNH0NCg0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3NlbGVjdGVkIDwtIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyAlPiUgDQogICBzZWxlY3QoY29udGFpbnMoInJlc3VsdCIpLCBzYW1wbGVfc2l6ZSwgcmlza19kaXN0X3RyYW5zbCkgJT4lIA0KICAgZ3JvdXBfYnkocmlza19kaXN0X3RyYW5zbCwgc2FtcGxlX3NpemUpICU+JSANCiAgIG11dGF0ZShzYW1wbGVfSUQgPSByb3dfbnVtYmVyKCkpDQoNCmRpZmZfZGlzdHJpYnV0aW9ucyA8LSB0cnVlX3Njb3JlX3VuaXF1ZSAlPiUgDQogICBsZWZ0X2pvaW4oY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3NlbGVjdGVkLCBieSA9IGMoIngiID0gInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbGVmdF9qb2luKGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc19zZWxlY3RlZCwgYnkgPSBjKCJ5IiA9ICJyaXNrX2Rpc3RfdHJhbnNsIiwgInNhbXBsZV9zaXplIiwgInNhbXBsZV9JRCIpLCBzdWZmaXggPSBjKCJfeCIsICJfeSIpKSAlPiUgDQogICBtdXRhdGUoZGlmZl9CQyA9IEJDX3Jlc3VsdF94IC0gQkNfcmVzdWx0X3ksDQogICAgICAgICAgZGlmZl9wb2wgPSBwb2xhcml6YXRpb25fcmVzdWx0X3ggLSBwb2xhcml6YXRpb25fcmVzdWx0X3kpDQoNCndpdGhpbl9pbnRlcnZhbF9kaWZmX2Rpc3RyaWJ1dGlvbnMgPC0gZGlmZl9kaXN0cmlidXRpb25zICU+JSANCiAgIG11dGF0ZSh3aXRoaW5fQkMgPSBpZl9lbHNlKHRydWVfZGlmZl9CQyA+PSBkaWZmX0JDIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0ICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfZGlmZl9CQyA8PSBkaWZmX0JDICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSwNCiAgICAgICAgICB3aXRoaW5fcG9sID0gaWZfZWxzZSh0cnVlX2RpZmZfcG9sID49IGRpZmZfcG9sIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0ICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfZGlmZl9wb2wgPD0gZGlmZl9wb2wgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQsIDEsIDApLA0KICAgICAgICAgIGNvbXBhcmlzb24gPSBwYXN0ZSh4LCB5LCBzZXAgPSAiIC0gIikNCiAgICkgJT4lIA0KICAgZ3JvdXBfYnkoY29tcGFyaXNvbiwgc2FtcGxlX3NpemUpICU+JSANCiAgIHN1bW1hcml6ZShwZXJjX3dpdGhpbl9CQyA9IHN1bSh3aXRoaW5fQkMpIC8gcmVwbGljYXRpb25zX3Blcl9zZXR0aW5nICogMTAwLA0KICAgICAgICAgICAgIHBlcmNfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCkgJT4lIA0KICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgicGVyYyIpLA0KICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1lYXN1cmUiLA0KICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwZXJjZW50YWdlIiwNCiAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAicGVyY193aXRoaW5fIikNCiAgIA0KDQp3aXRoaW5faW50ZXJ2YWxfZGlmZl9kaXN0cmlidXRpb25zICU+JSBnZ3Bsb3QoYWVzKGZhY3RvcihzYW1wbGVfc2l6ZSksIHBlcmNlbnRhZ2UsIGNvbCA9IGZhY3Rvcihjb21wYXJpc29uKSwgZ3JvdXAgPSBmYWN0b3IoY29tcGFyaXNvbikpKSsNCiAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpKw0KICAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEuMikrDQogICBmYWNldF93cmFwKH5tZWFzdXJlKSsNCiAgIHRoZW1lX21pbmltYWwoKSsNCiAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkrDQogICBsYWJzKHkgPSAicGVyY2VudGFnZSBvZiBzaW11bGF0aW9ucyB3aXRoaW4gMC4wNSBpbnRlcnZhbCBvZiB0cnVlIHNjb3JlIiwNCiAgICAgICAgeCA9ICJzYW1wbGUgc2l6ZSIsDQogICAgICAgIHRpdGxlID0gIkludGVydmFsIG9mICstIDAuMDUiLA0KICAgICAgICBjb2xvciA9ICJDb21wYXJpc29uIGJldHdlZW4iKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMyKSsNCiAgIHRoZW1lKGxlZ2VuZC5zcGFjaW5nID0gdW5pdCgwLjEsICJjbSIpLA0KICAgICAgICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwNCiAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsMCwwLDApLA0KICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYyguNSwuMDUpLA0KICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikNCiAgIA0KDQoNCmBgYA0KDQojIyMgSW50ZXJ2YWwgb2YgLjA1IHsuYWN0aXZlfQ0KYGBge3IsIGZpZy53aWR0aD0gMTR9DQoNCndpdGhpbl9pbnRlcnZhbF9kaWZmX2Rpc3RyaWJ1dGlvbnMgPC0gZGlmZl9kaXN0cmlidXRpb25zICU+JSANCiAgIG11dGF0ZSh3aXRoaW5fQkMgPSBpZl9lbHNlKHRydWVfZGlmZl9CQyA+PSBkaWZmX0JDIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlX2RpZmZfQkMgPD0gZGlmZl9CQyArIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdDIsIDEsIDApLA0KICAgICAgICAgIHdpdGhpbl9wb2wgPSBpZl9lbHNlKHRydWVfZGlmZl9wb2wgPj0gZGlmZl9wb2wgLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfZGlmZl9wb2wgPD0gZGlmZl9wb2wgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyLCAxLCAwKSwNCiAgICAgICAgICBjb21wYXJpc29uID0gcGFzdGUoeCwgeSwgc2VwID0gIiAtICIpDQogICApICU+JSANCiAgIGdyb3VwX2J5KGNvbXBhcmlzb24sIHNhbXBsZV9zaXplKSAlPiUgDQogICBzdW1tYXJpemUocGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgICAgICAgICBwZXJjX3dpdGhpbl9wb2wgPSBzdW0od2l0aGluX3BvbCkgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDApICU+JSANCiAgIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoInBlcmMiKSwNCiAgICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicGVyY2VudGFnZSIsDQogICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gInBlcmNfd2l0aGluXyIpDQogICANCg0Kd2l0aGluX2ludGVydmFsX2RpZmZfZGlzdHJpYnV0aW9ucyAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSBmYWN0b3IoY29tcGFyaXNvbiksIGdyb3VwID0gZmFjdG9yKGNvbXBhcmlzb24pKSkrDQogICBnZW9tX3BvaW50KHNpemUgPSAyKSsNCiAgIGdlb21fbGluZShsaW5ld2lkdGggPSAxLjIpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDI1IGludGVydmFsIG9mIHRydWUgc2NvcmUiLA0KICAgICAgICB4ID0gInNhbXBsZSBzaXplIiwNCiAgICAgICAgdGl0bGUgPSAiSW50ZXJ2YWwgb2YgKy0gMC4wMjUiLA0KICAgICAgICBjb2xvciA9ICJDb21wYXJpc29uIGJldHdlZW4iKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMyKSsNCiAgIHRoZW1lKGxlZ2VuZC5zcGFjaW5nID0gdW5pdCgwLjEsICJjbSIpLA0KICAgICAgICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwNCiAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsMCwwLDApLA0KICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYyguNSwuMDUpLA0KICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikNCmBgYA0KDQojIERpc2N1c3Npb24NCg0KQXMgb25lIGhhcyBleHBlY3RlZCwgdGhlICJuZWVkZWQiIHNhbXBsZSBzaXplIGluY3JlYXNlcyB0aGUgc3RyaWN0ZXIgd2Ugc2V0IHRoZSBpbnRlcnZhbHMgd2hpY2ggd2Ugd291bGQgYmUgc2F0aXNmaWVkIHdpdGguICANCg0KSWYgb25lIHdlcmUgdG8gb25seSBsb29rIGF0IHRoZSBwbG90cyB3aXRoIHRoZSBzdHJpY3RlciBpbnRlcnZhbDoNCg0KLSBPbiB0aGUgc2lkZSBvZiBiaW1vZGFsaXR5IGNvZWZmaWNpZW50LCBvbmUgY291bGQgYXJndWUgdG8gc2FtcGxlIGFyb3VuZCAxMjUwIHBhcnRpY2lwYW50cyBzbyB0aGF0IHdlIGhhdmUgYSBnb29kIGNoYW5jZSBvZiBhcm91bmQgODAlIHRvIGdldCBhIGdvb2QgZXN0aW1hdGUgZm9yIFsqKnJhcmUgZGlzdHJpYnV0aW9ucyoqXXtzdHlsZT0iY29sb3I6IGByIGNvbG9yc1szXWA7In0gKHNvIHRoYXQgaW4gY2FzZSB3ZSBkbyBzYW1wbGUgZnJvbSBhIHJhcmUgZGlzdHJpYnV0aW9uLCBpbiA4MCUgb2YgdGhlIHRpbWUsIHRoZSB0cnVlIGJpbW9kYWxpdHkgY29lZmZpY2llbnQgaXMgb2ZmIGJ5IG9ubHkgYSBzbWFsbCBhbW91bnQpLiBJZiBvbmUgd2FudHMgdG8gbWFrZSBzdXJlLCBldmVuIHNhbXBsaW5nIDIwMDAgcGFydGljaXBhbnRzIHdvdWxkIGJlIG5pY2UgdG8gaW5jcmVhc2UgdGhlIGNoYW5jZSB0byA5MCUsIGJ1dCBtYXliZSB0aGF0IGlzIHN0cmV0Y2hpbmcgdGhlIGJ1ZGdldCBhIGxpdHRsZSBiaXQuLi4NCg0KLSBGb3IgdGhlIG1lYXN1cmUgb2YgcG9sYXJpemF0aW9uLCBlc3RpbWF0aW5nIHRoZSB0cnVlIHNjb3JlIHZhbHVlIGlzIGhhcmRlc3QgaWYgb25lIGhhcyBhIFsqKnN0cm9uZ2x5IHBvbGFyaXplZCoqXXtzdHlsZT0iY29sb3I6IGByIGNvbG9yc1s0XWA7In0gZGlzdHJpYnV0aW9uLiBJbiB0aGlzIGNhc2UsIHdlIGhhdmUgYSA4MCUgY2hhbmNlIG9mIGdldHRpbmcgaXQgInJpZ2h0IiBpZiBvbmUgd291bGQgc2FtcGxlIDEyNTAgcGFydGljaXBhbnRzLCBvciA5MCUgZm9yIHNhbXBsaW5nIDIwMDAgcmVzcGVjdGl2ZWx5Lg0KDQpUaGUgbnVtYmVycyBoZXJlIGFyZSBvbmx5IHN1Z2dlc3Rpb25zLCBhbmQgYXJlIG9ubHkgYmFzZWQgb24gKipwcmVjaXNpb24qKiwgc28gdGhhdCB3ZSBjYW4gYmUgc3VyZSB0aGF0IHRoZSByZXN1bHRzIHdlIGdldCBhcmUgcmVsaWFibGUgZW5vdWdoLiBJbiBhbnkgY2FzZSwgdGhlc2UgbnVtYmVycyBhcmUgIm9ubHkiIHdvcnN0IGNhc2Ugc2NlbmFyaW9zLCBhcyB0aGUgb3RoZXIgcG9wdWxhdGlvbiBkaXN0cmlidXRpb25zIGFyZSBjb252ZXJnaW5nIGVhcmxpZXIuIFdlIGFyZSBhbHNvIG9ubHkgbG9va2luZyBhdCB0aGUgc3RyaWN0ZXIgc2NlbmFyaW8uLi4NCg0KV2hlbiBsb29raW5nIGZvciB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBkaXN0cmlidXRpb25zLCBvbmUgbmVlZCB0byBzYW1wbGUgMTUwMCBvciBldmVuIDI3NTAgcGVvcGxlIGZvciA4MCUgb3IgOTAlIGNoYW5jZSBvZiBiZWluZyBuZWFyIHRoZSB0cnVlIHNjb3JlLiAgDQoNCg0KIyMjIExpbWl0YXRpb25zDQoNCi0gRGlzY3Vzc2lvbiB3YXMgc29sZWx5IGJhc2VkIG9uIHRoZSBzdHJpY3RlciBpbnRlcnZhbCAoZS5nLiB0aGUgbGFzdCBwbG90KSwgbm90IG9uIHRoZSBsZW5pZW50IG9uZSB3aXRoIGEgYnJvYWRlciBpbnRlcnZhbC4NCi0gQXMgYWx3YXlzLCB0aGUgc2ltdWxhdGlvbiBzaG91bGQgYmUgdGFrZW4gd2l0aCBhIGdyYWluIG9mIHNhbHQsIGFzIHRoZXNlIHJlc3VsdHMgYXJlIG1hZGUgd2l0aCB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgYXMgImJlYXV0aWZ1bCIgYXMgSSBtYWRlIGl0IGluIHRoZSBpbnRyb2R1Y3Rpb24gc2VjdGlvbi4uLiANCi0gV2hldGhlciB3ZSBjYW4gdHJ1bHkgc2FtcGxlIGFzIHJhbmRvbSBhcyB3ZSB3YW50IHRvLCBvciB3ZSBnZXQgc29tZSBzdWJzYW1wbGVzIGlzIG5vdCBvZiBkZWJhdGUgaGVyZS4gVGhpcyB3b3JrIGp1c3QgYXNzdW1lcyB0aGF0IHRoZSB3aG9sZSBwb3B1bGF0aW9uIGhhcyBYIGRpc3RyaWJ1dGlvbi4NCi0gTm8gY29tcGFyaXNvbiBiZXR3ZWVuIGRpZmZlcmVudCBzYW1wbGUgZGlzdHJpYnV0aW9ucyB3ZXJlIG1hZGUgKGUuZy4gb3ZlcmxhcCBjb2VmZmljaWVudCksIHNvIHdlIHN0aWxsIGhhdmUgbm8gaWRlYSBob3cgYmlnIHRoZSBzYW1wbGUgc2l6ZSBuZWVkcyB0byBiZSBpZiB3ZSB3YW50IHRvIGRpc2NvdmVyIGEgZGlmZmVyZW5jZSBvZiBYIGJldHdlZW4gdHdvIGNvdW50cmllcy8gZ3JvdXBzICoqaW4gY2FzZSB0aGVyZSBpcyBhIGRpZmZlcmVuY2UqKi4NCg0KIyBDcmVkaXRzDQoNCg0KIyMgUiBQYWNrYWdlcyBVc2VkDQoNCi0gYWdybXQgKFJ1ZWRpbiBEICgyMDIzKS4gX2Fncm10OiBDYWxjdWxhdGUgQ29uY2VudHJhdGlvbiBhbmQgRGlzcGVyc2lvbiBpbiBPcmRlcmVkIFJhdGluZyBTY2FsZXNfLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjQyLjEyLA0KPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9YWdybXQ+LikgIA0KDQotIGRvUGFyYWxsZWwgKENvcnBvcmF0aW9uIE0sIFdlc3RvbiBTICgyMDIyKS4gX2RvUGFyYWxsZWw6IEZvcmVhY2ggUGFyYWxsZWwgQWRhcHRvciBmb3IgdGhlICdwYXJhbGxlbCcgUGFja2FnZV8uIFIgcGFja2FnZSB2ZXJzaW9uIDEuMC4xNywNCjxodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWRvUGFyYWxsZWw+LikgIA0KDQotIGZvcmVhY2ggKE1pY3Jvc29mdCwgV2VzdG9uIFMgKDIwMjIpLiBfZm9yZWFjaDogUHJvdmlkZXMgRm9yZWFjaCBMb29waW5nIENvbnN0cnVjdF8uIFIgcGFja2FnZSB2ZXJzaW9uIDEuNS4yLCA8aHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1mb3JlYWNoPi4pICANCg0KLSBrbml0ciAoWGllIFkgKDIwMjMpLiBfa25pdHI6IEEgR2VuZXJhbC1QdXJwb3NlIFBhY2thZ2UgZm9yIER5bmFtaWMgUmVwb3J0IEdlbmVyYXRpb24gaW4gUl8uIFIgcGFja2FnZSB2ZXJzaW9uIDEuNDUsIDxodHRwczovL3lpaHVpLm9yZy9rbml0ci8+LikgIA0KDQotIHBzeWNoIChXaWxsaWFtIFJldmVsbGUgKDIwMjMpLiBfcHN5Y2g6IFByb2NlZHVyZXMgZm9yIFBzeWNob2xvZ2ljYWwsIFBzeWNob21ldHJpYywgYW5kIFBlcnNvbmFsaXR5IFJlc2VhcmNoXy4gTm9ydGh3ZXN0ZXJuIFVuaXZlcnNpdHksIEV2YW5zdG9uLCBJbGxpbm9pcy4gUg0KcGFja2FnZSB2ZXJzaW9uIDIuMy45LCA8aHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1wc3ljaD4uKSAgDQoNCi0gUkNvbG9yQnJld2VyIChOZXV3aXJ0aCBFICgyMDIyKS4gX1JDb2xvckJyZXdlcjogQ29sb3JCcmV3ZXIgUGFsZXR0ZXNfLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjEtMywgPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9UkNvbG9yQnJld2VyPi4pICANCg0KLSBybWFya2Rvd24gKEFsbGFpcmUgSiwgWGllIFksIERlcnZpZXV4IEMsIE1jUGhlcnNvbiBKLCBMdXJhc2NoaSBKLCBVc2hleSBLLCBBdGtpbnMgQSwgV2lja2hhbSBILCBDaGVuZyBKLCBDaGFuZyBXLCBJYW5ub25lIFIgKDIwMjMpLiBfcm1hcmtkb3duOiBEeW5hbWljDQpEb2N1bWVudHMgZm9yIFJfLiBSIHBhY2thZ2UgdmVyc2lvbiAyLjI1LCA8aHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duPi4pICANCg0KLSB0aWR5dmVyc2UgKFdpY2toYW0gSCwgQXZlcmljayBNLCBCcnlhbiBKLCBDaGFuZyBXLCBNY0dvd2FuIExELCBGcmFuw6dvaXMgUiwgR3JvbGVtdW5kIEcsIEhheWVzIEEsIEhlbnJ5IEwsIEhlc3RlciBKLCBLdWhuIE0sIFBlZGVyc2VuIFRMLCBNaWxsZXIgRSwgQmFjaGUgU00sDQpNw7xsbGVyIEssIE9vbXMgSiwgUm9iaW5zb24gRCwgU2VpZGVsIERQLCBTcGludSBWLCBUYWthaGFzaGkgSywgVmF1Z2hhbiBELCBXaWxrZSBDLCBXb28gSywgWXV0YW5pIEggKDIwMTkpLiDigJxXZWxjb21lIHRvIHRoZSB0aWR5dmVyc2Uu4oCdIF9Kb3VybmFsIG9mDQpPcGVuIFNvdXJjZSBTb2Z0d2FyZV8sICo0Kig0MyksIDE2ODYuIGRvaToxMC4yMTEwNS9qb3NzLjAxNjg2IDxodHRwczovL2RvaS5vcmcvMTAuMjExMDUvam9zcy4wMTY4Nj4uKSAgDQoNCi0gdmlzZGF0IChUaWVybmV5IE4gKDIwMTcpLiDigJx2aXNkYXQ6IFZpc3VhbGlzaW5nIFdob2xlIERhdGEgRnJhbWVzLuKAnSBfSk9TU18sICoyKigxNiksIDM1NS4gZG9pOjEwLjIxMTA1L2pvc3MuMDAzNTUgPGh0dHBzOi8vZG9pLm9yZy8xMC4yMTEwNS9qb3NzLjAwMzU1PiwNCjxodHRwOi8vZHguZG9pLm9yZy8xMC4yMTEwNS9qb3NzLjAwMzU1Pi4pICANCg==